home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1995 April / Internet Tools.iso / ip / ppp / mac / macppp2.0.1-src.hqx / MacPPP 2.0.1 src / ppp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-30  |  19.2 KB  |  726 lines

  1. /*
  2.  *  ppp.c - ppp support routines
  3.  *
  4.  * Copyright 1991-1992 William Allen Simpson
  5.  *  Licensed for non-profit non-commercial distribution
  6.  *  in Merit's PPP LAP for MacTCP
  7.  *
  8.  * Copyright 1992-1993 Merit Network, Inc. and The Regents of the
  9.  *  University of Michigan.  Usage of this source code is restricted
  10.  *  to non-profit, non-commercial purposes.  The source is provided
  11.  *  "as-is", without warranty.
  12.  *
  13.  * This code has been derived solely from the Jan 1991 public
  14.  *  domain release of PPP included in Phil Karn's KA9Q.
  15.  */
  16. #include "ppp.h"
  17.  
  18. /* Convert PPP header in host form to network form */
  19. void
  20. htonppp(LapInfo *lap, register b_16 protocol, struct bufheader *bufptr)
  21. {
  22.     struct proto_s *proto_p = &(lap->lcp_i);
  23.     register b_8 *cp;
  24.     register long len;
  25.     Boolean full_lcp, full_prot, full_acf;
  26.  
  27.     full_lcp = ( protocol == PPP_LCP_PROTOCOL );
  28.     full_prot = ( full_lcp
  29.         || !(proto_p->remote.work_negotiate & LCP_N_PFC)
  30.         || protocol >= 0x00FF );
  31.     full_acf = ( full_lcp
  32.         || !(proto_p->remote.work_negotiate & LCP_N_ACFC) );
  33.  
  34.     len = full_acf * 2 + full_prot + 1;
  35.  
  36.     /* Prepend header onto packet data */
  37.     bufptr->length += len;
  38.     cp = (bufptr->dataptr -= len);
  39.     
  40.     /* Load header with proper values */
  41.     if ( full_acf ) {
  42.         *cp++ = HDLC_ALL_ADDR;
  43.         *cp++ = HDLC_UI;
  44.     }
  45.     if ( full_prot )
  46.         *cp++ = protocol >> 8;
  47.  
  48.     *cp++ = protocol;
  49. }
  50.  
  51. /* Check for PPP Network-Layer Protocol Phase */
  52. void ppp_ready(LapInfo *lap)
  53. {
  54.     if ( !(lap->ppp_flags & PPP_AP_REMOTE) ) {
  55.         /* no pending authentication */
  56.         lap->ppp_phase = pppNETWORK;
  57.         lap->ppp_upsince = TickCount();
  58.         fsm_up( &(lap->ppp_fsm[IPcp]) );        /* send an UP event to IPcp */
  59.     }
  60. }
  61.  
  62. void
  63. ppp_init (register LapInfo *lap )
  64. {
  65.     register struct fsm_s *fsm_p;
  66.  
  67.     lap->ppp_phase = pppDEAD;
  68.  
  69.     /* init LCP */
  70.     fsm_p = &(lap->ppp_fsm[Lcp]);
  71.     fsm_p->pdc.protocol = PPP_LCP_PROTOCOL;
  72.     fsm_p->pdc.option_limit = LCP_OPTION_LIMIT;
  73.     fsm_p->pdc.option_lengths = lap->lcp_option_length_p;
  74.     fsm_p->pdc.fsmi = Lcp;
  75.     fsm_p->pdc.try_nak = LCP_NAK_TRY;
  76.     fsm_p->pdc.try_terminate = LCP_TERM_TRY;
  77.     fsm_p->lap = lap;
  78.     fsm_p->pdv = &(lap->lcp_i);
  79.     fsm_init(fsm_p);
  80.  
  81.     /* init IPCP */
  82.     fsm_p = &(lap->ppp_fsm[IPcp]);
  83.     fsm_p->pdc.protocol = PPP_IPCP_PROTOCOL;
  84.     fsm_p->pdc.fsmi = IPcp;
  85.     fsm_p->pdc.try_nak = IPCP_NAK_TRY;
  86.     fsm_p->pdc.try_terminate = IPCP_TERM_TRY;
  87.     fsm_p->pdc.option_limit = IPCP_OPTION_LIMIT;
  88.     fsm_p->pdc.option_lengths = lap->ipcp_option_length_p;
  89.     fsm_p->lap = lap;
  90.     fsm_p->pdv = &(lap->ipcp_i);
  91.     fsm_init(fsm_p);
  92.  
  93.     /* init PAP */
  94.     fsm_p = &(lap->ppp_fsm[Pap]);
  95.     fsm_p->pdc.protocol = PPP_PAP_PROTOCOL;
  96.     fsm_p->pdc.fsmi = Pap;
  97.     fsm_p->lap = lap;
  98.     fsm_p->pdv = &(lap->pap_i);
  99.     fsm_p->state = fsmCLOSED;
  100. }
  101.  
  102. /* Keep track of changes in I-O status */
  103. short
  104. ppp_iostatus(LapInfo *lap, short command)
  105. {
  106.     switch ( command ) {
  107.     case PARAM_UP:
  108.         if ( lap->ppp_phase == pppDEAD ) {
  109.             lap->ppp_phase = pppESTABLISH;
  110.         }
  111.         fsm_up( &(lap->ppp_fsm[Lcp]) );
  112.         return 0;
  113.  
  114.     case PARAM_DOWN:
  115.         fsm_down( &(lap->ppp_fsm[Lcp]) );
  116.         lap->ppp_phase = pppDEAD;
  117.         return 0;
  118.     };
  119.     return -1;
  120. }
  121.  
  122. /* Process configuration ACK sent by remote host */
  123. short proc_ack(fsm_p, config, bufptr)
  124. struct fsm_s *fsm_p;
  125. struct config_hdr *config;
  126. struct bufheader *bufptr;
  127. {
  128.     struct bufheader *req_buf;
  129.  
  130.     /* ID field must match last request we sent */
  131.     if (config->id != fsm_p->lastid) {
  132.         PPP_DEBUG_CHECKS("\pACK: wrong ID");
  133.         goto bad_return;
  134.     }
  135.  
  136.     /* Get a copy of last request we sent */
  137.     if ( (req_buf = makereq(fsm_p)) == nil)
  138.         goto bad_return;
  139.  
  140.     /* Overall buffer length should match */
  141.     if (config->len != req_buf->length) {
  142.         PPP_DEBUG_CHECKS("\pACK: buffer length mismatch");
  143.             goto bad_return2;
  144.         } else {
  145.         register short req_char;
  146.         register short ack_char;
  147.  
  148.         /* Each byte should match */
  149.         while ((req_char = yankbyte(req_buf)) != -1) {
  150.             if ((ack_char = yankbyte(bufptr)) == -1
  151.              || ack_char != req_char ) {
  152.                 PPP_DEBUG_CHECKS("\pACK: data mismatch");
  153.                 goto bad_return2;
  154.             }
  155.         }
  156.     }
  157.     release(req_buf);
  158.     release(bufptr);
  159.     return 0;
  160.  
  161. bad_return2:
  162.     release(req_buf);
  163. bad_return:
  164.     release(bufptr);
  165.     return -1;
  166. }
  167.  
  168. /************************************************************************/
  169. /* Process configuration NAK sent by remote host */
  170. short proc_nak(fsm_p, config, bufptr)
  171. struct fsm_s *fsm_p;
  172. struct config_hdr *config;
  173. struct bufheader *bufptr;
  174. {
  175.     struct proto_s *proto_p = (struct proto_s *) (fsm_p->pdv);
  176.     long signed_length = config->len;
  177.     struct option_hdr option;
  178.     short last_option = 0;
  179.     short result;
  180.  
  181.     /* ID field must match last request we sent */
  182.     if (config->id != fsm_p->lastid) {
  183.         PPP_DEBUG_CHECKS("\pNAK: wrong ID");
  184.         goto bad_return;
  185.     }
  186.  
  187.     /* First, process in order.  Then, process extra "important" options */
  188.     while (signed_length > 0  &&  ntohopt(&option, bufptr) != -1) {
  189.         if ((signed_length -= option.len) < 0) {
  190.             PPP_DEBUG_CHECKS("\pNAK: bad header length");
  191.             goto bad_return;
  192.         }
  193.         if ( option.type > fsm_p->pdc.option_limit ) {
  194.             PPP_DEBUG_CHECKS("\pNAK: option out of range");
  195.         } else if ( option.type < last_option
  196.           || !(proto_p->local.work_negotiate & (1 << option.type)) ) {
  197.             if (proto_p->local.work_negotiate & (1 << option.type)) {
  198.                 PPP_DEBUG_CHECKS("\pNAK: option out of order");
  199.                 goto bad_return;
  200.             }
  201.             proto_p->local.work_negotiate |= (1 << option.type);
  202.             last_option = fsm_p->pdc.option_limit + 1;
  203.         } else {
  204.             last_option = option.type;
  205.         }
  206.         if ( ( result = option_check( bufptr, fsm_p, &option, FALSE ) ) == -1 ) {
  207.             PPP_DEBUG_CHECKS("\pNAK: ran out of data");
  208.             goto bad_return;
  209.         }
  210.         /* update the negotiation status */
  211.         if ( result == CONFIG_REJ
  212.           && option.type <= fsm_p->pdc.option_limit ) {
  213.             proto_p->local.work_negotiate &= ~(1 << option.type);
  214.         }
  215.     }
  216.     release(bufptr);
  217.     return 0;
  218.  
  219. bad_return:
  220.     release(bufptr);
  221.     return -1;
  222. }
  223.  
  224. /* Process configuration reject sent by remote host */
  225. short
  226. proc_reject(fsm_p, config, bufptr)
  227. struct fsm_s *fsm_p;
  228. struct config_hdr *config;
  229. struct bufheader *bufptr;
  230. {
  231.     struct proto_s *proto_p = (struct proto_s *) (fsm_p->pdv);
  232.     long signed_length = config->len;
  233.     struct option_hdr option;
  234.     short last_option = 0;
  235.  
  236.     /* ID field must match last request we sent */
  237.     if (config->id != fsm_p->lastid) {
  238.         PPP_DEBUG_CHECKS("\pREJ: wrong ID");
  239.         goto bad_return;
  240.     }
  241.  
  242.     /* Process in order, checking for errors */
  243.     while (signed_length > 0  &&  ntohopt(&option, bufptr) != -1) {
  244.         register short k;
  245.  
  246.         if ((signed_length -= option.len) < 0) {
  247.             PPP_DEBUG_CHECKS("\pREJ: bad header length");
  248.             goto bad_return;
  249.         }
  250.         if ( option.type > fsm_p->pdc.option_limit ) {
  251.             PPP_DEBUG_CHECKS("\pIPCP REJ: option out of range");
  252.         } else if (option.type < last_option
  253.          || !(proto_p->local.work_negotiate & (1 << option.type))) {
  254.             PPP_DEBUG_CHECKS("\pREJ: option out of order");
  255.             goto bad_return;
  256.         }
  257.         for ( k = option.len - OPTION_HDR_LEN; k-- > 0; ) {
  258.             if ( yankbyte(bufptr) == -1 ) {
  259.                 PPP_DEBUG_CHECKS("\pREJ: ran out of data");
  260.                 goto bad_return;
  261.             }
  262.         }
  263.  
  264.         if ( (last_option = option.type) <= fsm_p->pdc.option_limit) {
  265.             proto_p->local.work_negotiate &= ~(1 << option.type);
  266.             if (fsm_p->pdc.fsmi == IPcp && 
  267.                     option.type == IPCP_ADDRESS) { /* try old style address negotiationg */
  268.                 proto_p->local.work_negotiate |= IPCP_N_ADDRESSES;
  269.                 if (proto_p->remote.want_negotiate & IPCP_N_ADDRESS)
  270.                     proto_p->remote.want_negotiate ^=
  271.                         IPCP_N_ADDRESS | IPCP_N_ADDRESSES;
  272.             }
  273.         }
  274.     }
  275.     release(bufptr);
  276.     return 0;
  277.  
  278. bad_return:
  279.     release(bufptr);
  280.     return -1;
  281. }
  282.  
  283. /* Check the options, updating the working values.
  284.  * Returns -1 if ran out of data, ACK/NAK/REJ as appropriate.
  285.  */
  286. short
  287. option_check(struct bufheader *bufptr, struct fsm_s *fsm_p,
  288.             struct option_hdr *option_p, short request)
  289. {
  290.     short toss = option_p->len - OPTION_HDR_LEN;
  291.     register int option_result = CONFIG_ACK;        /* Assume good values */
  292.     short test;
  293.     register long temp_val;
  294.     register b_8 optype = option_p->type;
  295.     b_8 *cp = bufptr->dataptr;
  296.     struct proto_s *proto_p = ( (struct proto_s *) (fsm_p->pdv) );
  297.     struct    option_s *side_p;
  298.     
  299.     if (request)
  300.         side_p = &(proto_p->remote);
  301.     else
  302.         side_p = &(proto_p->local);
  303.  
  304.     switch (fsm_p->pdc.fsmi) {
  305.     case Lcp: {
  306.     
  307.         if ( !(side_p->will_negotiate & (1 << optype))) {
  308.             option_result = CONFIG_REJ;        /* if we aren't willing to negotiate */
  309.             break;
  310.         }
  311.  
  312.         switch(optype) {
  313.         case LCP_MRU:
  314.             side_p->work.lcp_option.mru = yank16(bufptr);
  315.             toss -= 2;
  316.  
  317.             /* Check if new value is appropriate */
  318.             if (side_p->work.lcp_option.mru < LCP_MRU_LO) {
  319.                 side_p->work.lcp_option.mru = LCP_MRU_LO;
  320.                 option_result = CONFIG_NAK;
  321.             } else if (side_p->work.lcp_option.mru > LCP_MRU_HI) {
  322.                 side_p->work.lcp_option.mru = LCP_MRU_HI;
  323.                 option_result = CONFIG_NAK;
  324.             }
  325.             if ( (side_p->want_negotiate & LCP_N_MRU) && !request
  326.               && side_p->work.lcp_option.mru > side_p->want.lcp_option.mru ) {
  327.                 side_p->work.lcp_option.mru = side_p->want.lcp_option.mru;
  328.                 option_result = CONFIG_NAK;
  329.             }
  330.             break;
  331.  
  332.         case LCP_ACCM:
  333.             side_p->work.lcp_option.accm = yank32(bufptr);
  334.             toss -= 4;
  335.             /* Remote host may ask to escape more control  */
  336.             /* characters than we require, but must escape */
  337.             /* at least the control chars that we require. */
  338.             if ( (!request || (side_p->want_negotiate & LCP_N_ACCM))
  339.               && side_p->work.lcp_option.accm !=
  340.                    (side_p->work.lcp_option.accm | side_p->want.lcp_option.accm) ) {
  341.                 side_p->work.lcp_option.accm |= side_p->want.lcp_option.accm;
  342.                 option_result = CONFIG_NAK;
  343.             }    
  344.             break;
  345.  
  346.         case LCP_AUTHENT:
  347.             side_p->work.lcp_option.authentication = yank16(bufptr);
  348.             toss -= 2;
  349.  
  350.             /* Check if new value is appropriate */
  351.             switch ( side_p->work.lcp_option.authentication ) {
  352.             case PPP_PAP_PROTOCOL:
  353.             /* Yes */
  354.                 break;
  355.             default:
  356.                 option_p->len = 4;        /* length of PAP option */
  357.                 side_p->work.lcp_option.authentication = PPP_PAP_PROTOCOL;
  358.                 option_result = CONFIG_NAK;
  359.                 break;
  360.             };
  361.             break;
  362.  
  363.         case LCP_MAGIC:
  364.             side_p->work.lcp_option.magic_number = yank32(bufptr);
  365.             toss -= 4;
  366.  
  367.             /* Ensure that magic numbers are different */
  368.             if (side_p->work.lcp_option.magic_number == 0L
  369.              || proto_p->remote.work.lcp_option.magic_number == \
  370.                  proto_p->local.work.lcp_option.magic_number) {
  371.                 side_p->work.lcp_option.magic_number += TickCount() * (long) bufptr;
  372.                 option_result = CONFIG_NAK;
  373.             }
  374.             break;
  375.  
  376.         case LCP_PFC:
  377.             break;
  378.  
  379.         case LCP_ACFC:
  380.             break;
  381.  
  382.         default:
  383.             option_result = CONFIG_REJ;
  384.         }
  385.       }
  386.           break;
  387.       case IPcp: {
  388.  
  389.         if ( !(side_p->will_negotiate & (1 << optype))) {
  390.             option_result = CONFIG_REJ;        /* if we aren't willing to negotiate */
  391.             break;
  392.         }
  393.  
  394.         switch(optype) {
  395.         case IPCP_ADDRESSES:
  396.         case IPCP_ADDRESS:
  397.             side_p->work.ipcp_option.address = get32(cp);    /* get address */
  398.             if (optype == IPCP_ADDRESSES)
  399.                 side_p->work.ipcp_option.other = get32(cp+4);    /* if addresses, get other address */
  400.             if ( !request ) {
  401.                 /* override any undesirable changes */
  402.                 if (proto_p->remote.want.ipcp_option.address != 0L) {
  403.                     proto_p->local.work.ipcp_option.other
  404.                         = proto_p->remote.want.ipcp_option.address;
  405.                 }
  406.  
  407.                 if (proto_p->local.want.ipcp_option.address != 0L) {
  408.                     proto_p->local.work.ipcp_option.address
  409.                         = proto_p->local.want.ipcp_option.address;
  410.                 }
  411.                 break;
  412.             }
  413.  
  414.             /* Ensure that addresses match */
  415.             if (proto_p->remote.work.ipcp_option.address ==
  416.                     proto_p->remote.want.ipcp_option.address) {
  417.                 if (proto_p->remote.want.ipcp_option.address == 0L) {
  418.                     /* don't know address either */
  419.                     option_result = CONFIG_REJ;
  420.                 }
  421.             } else if (proto_p->remote.want.ipcp_option.address == 0L) {
  422.                 proto_p->local.work.ipcp_option.other =
  423.                         proto_p->remote.work.ipcp_option.address;
  424.             } else {
  425.                 proto_p->remote.work.ipcp_option.address =
  426.                     proto_p->remote.want.ipcp_option.address;
  427.                 option_result = CONFIG_NAK;
  428.             }
  429.  
  430.             if (optype == IPCP_ADDRESSES) {
  431.                 if (proto_p->remote.work.ipcp_option.other ==
  432.                         proto_p->local.want.ipcp_option.address) {
  433.                     if (proto_p->local.want.ipcp_option.address == 0L) {
  434.                         /* don't know address either */
  435.                         option_result = CONFIG_REJ;
  436.                     }
  437.                 } else if (proto_p->local.want.ipcp_option.address == 0L) {
  438.                     proto_p->local.work.ipcp_option.address =
  439.                             proto_p->remote.work.ipcp_option.other;
  440.                 } else {
  441.                     option_result = CONFIG_NAK;
  442.                     proto_p->remote.work.ipcp_option.other =
  443.                         proto_p->local.want.ipcp_option.address;
  444.                 }
  445.             }
  446.             break;
  447.  
  448.         case IPCP_COMPRESS:
  449.             side_p->work.ipcp_option.compression = yank16(bufptr);
  450.             toss -= 2;
  451.             /* Check if requested type is acceptable */
  452.             switch ( side_p->work.ipcp_option.compression ) {
  453.             case PPP_COMPR_PROTOCOL:
  454.                 if ( toss == 0 ) { /* check if old-style VJ option */
  455.                     option_result = CONFIG_REJ;
  456.                     break;
  457.                 }
  458.                 if ( (test = yankbyte(bufptr)) == -1 )
  459.                     return -1;
  460.                 if ( (side_p->work.ipcp_option.slots = test + 1) < IPCP_SLOT_LO) {
  461.                     side_p->work.ipcp_option.slots = IPCP_SLOT_LO;
  462.                     option_result = CONFIG_NAK;
  463.                 } else if (side_p->work.ipcp_option.slots > IPCP_SLOT_HI && !request) {
  464.                     side_p->work.ipcp_option.slots = IPCP_SLOT_HI;
  465.                     option_result = CONFIG_NAK;
  466.                 }
  467.  
  468.                 if ( (test = yankbyte(bufptr)) == -1 )
  469.                     return -1;
  470.                 if ( (side_p->work.ipcp_option.slot_compress = test) > 1 ) {
  471.                     side_p->work.ipcp_option.slot_compress = 1;
  472.                     option_result = CONFIG_NAK;
  473.                 }
  474.                 toss -= 2;
  475.                 break;
  476.  
  477.             default:
  478.                 if ( side_p->want_negotiate & IPCP_N_COMPRESS ) {
  479.                     side_p->work.ipcp_option.compression = side_p->want.ipcp_option.compression;
  480.                     side_p->work.ipcp_option.slots = side_p->want.ipcp_option.slots;
  481.                     side_p->work.ipcp_option.slot_compress = side_p->want.ipcp_option.slot_compress;
  482.                 } else {
  483.                     side_p->work.ipcp_option.compression = PPP_COMPR_PROTOCOL;
  484.                     side_p->work.ipcp_option.slots = IPCP_SLOT_DEFAULT;
  485.                     side_p->work.ipcp_option.slot_compress = IPCP_SLOT_COMPRESS;
  486.                 }
  487.                 option_result = CONFIG_NAK;
  488.                 break;
  489.             };
  490.             break;
  491.  
  492.         default:
  493.             option_result = CONFIG_REJ;
  494.             break;
  495.         }
  496.      }
  497.      }
  498.     if ( toss < 0 )
  499.         return -1;
  500.  
  501.     if ( !request || (option_result != CONFIG_REJ)) {
  502.         /* toss extra bytes in option */
  503.         while( toss-- > 0 ) {
  504.             if ( yankbyte(bufptr) == -1 )
  505.                 return -1;
  506.         }
  507.     }
  508.     return (option_result);
  509. }
  510.  
  511. /* Check options requested by the remote host */
  512. short proc_request(fsm_p, config, bufptr)
  513. struct fsm_s *fsm_p;
  514. struct config_hdr *config;
  515. struct bufheader *bufptr;
  516. {
  517.     long signed_length = config->len;
  518.     struct bufheader *reply_buf;    /* reply packet */
  519.     short reply_result = CONFIG_ACK;        /* reply to request */
  520.     b_16 desired;                /* desired to negotiate */
  521.     struct option_hdr option;        /* option header storage */
  522.     short option_result;            /* option reply */
  523.     struct bufheader *rejdata;        /* for CONFIG rejects to return data */
  524.     struct proto_s *proto_p = (struct proto_s *) (fsm_p->pdv);
  525.  
  526.     proto_p->remote.work_negotiate = FALSE;    /* clear flags */
  527.  
  528.     if ((reply_buf = getbuffer()) == nil)
  529.         goto bad_return2;
  530.  
  531.     /* Process options requested by remote host */
  532.     while (signed_length > 0  &&  ntohopt(&option, bufptr) != -1) {
  533.         if ((signed_length -= option.len) < 0) {
  534.             PPP_DEBUG_CHECKS("\pREQ: bad header length");
  535.             goto bad_return;
  536.         }
  537.  
  538.         if ( ( option_result = option_check(bufptr, fsm_p,
  539.                  &option, TRUE ) ) == -1 ) {
  540.             PPP_DEBUG_CHECKS("\pREQ: ran out of data");
  541.             goto bad_return;
  542.         }
  543.  
  544.         if ( option_result < reply_result ) {
  545.             continue;
  546.         } else if ( option_result > reply_result ) {
  547.             /* Discard current list of replies */
  548.             release(reply_buf);
  549.             reply_buf = getbuffer();
  550.             reply_result = option_result;
  551.         }
  552.  
  553.         /* remember that we processed option */
  554.         if ( option_result != CONFIG_REJ ) {
  555.             proto_p->remote.work_negotiate |= (1 << option.type);
  556.             rejdata = nil;
  557.         } else {
  558.             rejdata = bufptr;
  559.         }
  560.  
  561.         /* Add option response to the return list */
  562.         add_option(fsm_p, reply_buf, &(proto_p->remote.work), option.type, option.len, rejdata);
  563.     }
  564.  
  565.     /* Now check for any missing options which are desired */
  566.     if ( fsm_p->retry_nak > 0
  567.      &&  (desired = proto_p->remote.want_negotiate &
  568.              ~proto_p->remote.work_negotiate) != 0 ) {
  569.         switch ( reply_result ) {
  570.         case CONFIG_ACK:
  571.             release(reply_buf);
  572.             reply_buf = getbuffer();
  573.             reply_result = CONFIG_NAK;
  574.             /* fallthru */
  575.         case CONFIG_NAK:
  576.             makeoptions(fsm_p, reply_buf, &(proto_p->remote.want), desired );
  577.             fsm_p->retry_nak--;
  578.             break;
  579.         case CONFIG_REJ:
  580.             /* do nothing */
  581.             break;
  582.         };
  583.     } else if ( reply_result == CONFIG_NAK ) {
  584.         /* if too many NAKs, reject instead */
  585.         if ( fsm_p->retry_nak > 0 )
  586.             fsm_p->retry_nak--;
  587.         else
  588.             reply_result = CONFIG_REJ;
  589.     }
  590.  
  591.     /* Send ACK/NAK/REJ to remote host */
  592.     fsm_send(fsm_p, reply_result, config->id, reply_buf);
  593.     release(bufptr);
  594.     return (reply_result != CONFIG_ACK);
  595.  
  596. bad_return:
  597.     release(reply_buf);
  598. bad_return2:
  599.     release(bufptr);
  600.     return -1;
  601. }
  602.  
  603. /* Build a list of options */
  604. void makeoptions(struct fsm_s *fsm_p, struct bufheader *bufptr,
  605.                         union value_s *value_p, b_16 negotiating)
  606. {
  607.     register short o_type;
  608.  
  609.     for ( o_type = 1; o_type <= fsm_p->pdc.option_limit ; o_type++ ) {
  610.         if (negotiating & (1 << o_type)) {
  611.             add_option(fsm_p, bufptr, value_p, o_type,
  612.                          *(fsm_p->pdc.option_lengths + o_type), nil);
  613.         }
  614.     }
  615. }
  616.  
  617. /* add an option to a packet */
  618. void add_option(struct fsm_s *fsm_p, struct bufheader *bufptr,
  619.     union value_s *value_p, b_8 o_type, b_8 o_length, struct bufheader *copy_buffer)
  620. {
  621.     register b_8 *cp;
  622.     register short copylen;
  623.     
  624.     cp = bufptr->dataptr + bufptr->length;
  625.     *cp++ = o_type;
  626.     *cp++ = o_length;
  627.     bufptr->length += o_length;
  628.  
  629.     if (copy_buffer != nil) {    /* if we should just copy the data */
  630.         copylen = o_length - OPTION_HDR_LEN;
  631.         while ( copylen-- > 0 ) {
  632.             *cp++ = yankbyte(copy_buffer);
  633.         }
  634.         return;
  635.     }
  636.     if (fsm_p->pdc.fsmi == Lcp) {  /* LCP Options */
  637.         switch ( o_type ) {
  638.         case LCP_MRU:
  639.             put16(cp, value_p->lcp_option.mru);
  640.             break;
  641.  
  642.         case LCP_ACCM:
  643.             put32(cp, value_p->lcp_option.accm);
  644.             break;
  645.  
  646.         case LCP_AUTHENT:
  647.             put16(cp, value_p->lcp_option.authentication);
  648.             break;
  649.  
  650.         case LCP_MAGIC:
  651.             put32(cp, value_p->lcp_option.magic_number);
  652.             break;
  653.  
  654.         case LCP_PFC:
  655.         case LCP_ACFC:
  656.             break;
  657.  
  658.         }
  659.     } else  {        /* IPCP options */
  660.         switch ( o_type ) {
  661.         case IPCP_ADDRESSES:
  662.             cp = put32(cp, value_p->ipcp_option.address);
  663.             put32(cp, value_p->ipcp_option.other);
  664.             break;
  665.  
  666.         case IPCP_ADDRESS:
  667.             put32(cp, value_p->ipcp_option.address);
  668.             break;
  669.  
  670.         case IPCP_COMPRESS:
  671.             cp = put16(cp, value_p->ipcp_option.compression);
  672.             if ( value_p->ipcp_option.compression == PPP_COMPR_PROTOCOL ) {
  673.                 *cp++ = value_p->ipcp_option.slots - 1;
  674.                 *cp++ = value_p->ipcp_option.slot_compress;
  675.             }
  676.             break;
  677.         }
  678.     }
  679. }
  680.  
  681. /* Build a request to send to remote host */
  682. struct bufheader *
  683. makereq(struct fsm_s *fsm_p)
  684. {
  685.     struct bufheader *req_buffer;
  686.     struct proto_s *proto_p = (struct proto_s *) (fsm_p->pdv);
  687.  
  688.     if (fsm_p->pdc.fsmi == Pap) {
  689.         struct pap_s *pap_p = fsm_p->pdv;
  690.         register b_8 *cp;
  691.         short len, userlen, passlen;
  692.  
  693.         if ( pap_p->username[0] == -1
  694.          ||  pap_p->password[0] == -1 ) {
  695.             PPP_DEBUG_CHECKS("\pNULL username or password");
  696.             return nil;
  697.         }
  698.  
  699.         userlen = pap_p->username[0];
  700.         passlen = pap_p->password[0];
  701.  
  702.         len = 2 + userlen + passlen;
  703.         if ((req_buffer = getbuffer()) == nil)
  704.             return nil;
  705.  
  706.         /* Load user id and password for authenticate packet */
  707.         cp = req_buffer->dataptr;
  708.         *cp++ = userlen;
  709.         if (userlen > 0) {
  710.             BlockMove(&pap_p->username[1], cp, (long) userlen);
  711.             cp += userlen;
  712.         }
  713.         *cp++= passlen;
  714.         if ( passlen > 0 )
  715.             BlockMove(&pap_p->password[1], cp, (long) passlen);
  716.                 
  717.         req_buffer->length += len;
  718.         return(req_buffer);
  719.     }
  720.     
  721.     if ( (req_buffer = getbuffer()) != nil)
  722.         makeoptions( fsm_p, req_buffer, &(proto_p->local.work),
  723.                         proto_p->local.work_negotiate );
  724.     return(req_buffer);
  725. }
  726.